package com.nulldev.util.concurrency.threadIt.v4.emapi.testing.bench;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutionPreference;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.compat.ExecutableFuture;
import com.nulldev.util.concurrency.threadIt.v4.emapi.compat.ExecutableRunnableFuture;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExecutorFeatureExtensions;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExtendedExecutor;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.concurrency.threadIt.v4.utils.impls.TEConditions;
import com.nulldev.util.data.Variables;
import com.nulldev.util.internal.backport.concurrency9.Lists;

public class BenchmarkExecutor extends AbstractExecutorService implements ExtendedExecutor {

	private boolean __is_shutdown;
	private final AtomicLong count = new AtomicLong();

	public AtomicLong get() {
		return this.count;
	}

	@Override
	public void shutdown() {
		this.__is_shutdown = true;
	}

	@Override
	public List<Runnable> shutdownNow() {
		return Lists.of();
	}

	@Override
	public boolean isShutdown() {
		return this.__is_shutdown;
	}

	@Override
	public boolean isTerminated() {
		return this.__is_shutdown;
	}

	@Override
	public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
		return this.__is_shutdown = true;
	}

	@Override
	public void execute(final Runnable r) {
		this.count.incrementAndGet();
	}

	@Override
	public boolean executorSupportsSlotCount() {
		return true;
	}

	private static final ExecutorFeatureExtensions[] SUPPORTED = new ExecutorFeatureExtensions[]
		{ ExecutorFeatureExtensions.EEX_core_executor_slots, ExecutorFeatureExtensions.EEX_query_slots_supported,
				ExecutorFeatureExtensions.EEX_core_submit_all_tasks, ExecutorFeatureExtensions.EEX_core_drain_task_queue,
				ExecutorFeatureExtensions.EEX_core_execute_with_preference, ExecutorFeatureExtensions.EEX_query_slots_supported,
				ExecutorFeatureExtensions.EEX_query_batching_supported, ExecutorFeatureExtensions.EEX_extended_unwinder_debugging,
				ExecutorFeatureExtensions.EEX_query_thread_affinity_supported, ExecutorFeatureExtensions.EEX_executor_low_priority_threads,
				ExecutorFeatureExtensions.EEX_executor_attemptable_submissions, ExecutorFeatureExtensions.EEX_executor_attemptable_batch_submissions,
				ExecutorFeatureExtensions.EEX_executor_attemptable_drain_submissions, ExecutorFeatureExtensions.EEX_query_executor_forking,
				ExecutorFeatureExtensions.EEX_executor_sched_accepts_always, ExecutorFeatureExtensions.EEX_compat_native_concurrency_api,
				ExecutorFeatureExtensions.EEX_executor_attemptable_submissions2, ExecutorFeatureExtensions.EEX_query_debug,
				ExecutorFeatureExtensions.EEX_query_executor_large_batches,

				ExecutorFeatureExtensions.EEX_executor_native_batching };

	@Override
	public ExecutorFeatureExtensions[] supported() {
		return SUPPORTED;
	}

	@Override
	public boolean executorSupportsBatching() {
		return true;
	}

	protected Object[] make_task(final Runnable r) {
		return make_task(r, 0);
	}

	protected Object[] make_task(final Runnable r, final int flags) {
		TEConditions.validRunnable(r);
		return new Object[]
			{ r, flags };
	}

	@Override
	public void batch(final Runnable command) {
		if (command == null)
			return;
		this.count.incrementAndGet();
	}

	@Override
	public void batchAll(final Runnable[] r) {
		if (r == null || r.length == 0)
			return;
		this.count.addAndGet(r.length);
	}

	@Override
	public boolean supportsFeature(final ExecutorFeatureExtensions feature) {
		return ArrayUtils.contains(SUPPORTED, feature);
	}

	@Override
	public int executor_getFreeSlots() {
		return 64;
	}

	public int getThreadLimit() {
		return 64;
	}

	@Override
	public void execute(final Runnable command, final ExecutionPreference e) {
		if (command == null)
			return;
		this.count.incrementAndGet();
	}

	@Override
	public void executor_submitAll(final List<Runnable> r) {
		if (r == null || r.isEmpty())
			return;
		this.count.incrementAndGet();
	}

	@Override
	public void executor_drainTasks(final Queue<Runnable> async_queue, final int batchSize) {
		if (async_queue == null || async_queue.isEmpty())
			return;
		if (batchSize <= 0)
			return;
		final Runnable[] rq = new Runnable[batchSize];
		for (int i = 0; i < batchSize; i++) {
			Runnable r = null;
			try {
				r = async_queue.poll();
			} catch (Throwable t) {
				t.printStackTrace();
			} finally {
				if (r == null)
					continue;
				rq[i] = r;
			}
		}
		this.count.addAndGet(rq.length);
	}

	/* EEX_query_thread_affinity_supported */

	@Override
	public boolean executorSupportsThreadAffinity() {
		return false;
	}

	@Override
	public boolean executorSupportsThreadPinning() {
		return false;
	}

	/* EEX_executor_low_priority_threads */

	@Override
	public boolean supportsLPT() {
		return true;
	}

	@Override
	public boolean isLPTActive() {
		return true;
	}

	@Override
	public void setLPTActive(final boolean state) {
		this.unsupported(ExecutorFeatureExtensions.EEX_executor_low_priority_threads);
	}

	@Override
	public int[] getLPTs() {
		return new int[0];
	}

	@Override
	public void submitLP(final Runnable command) {
		this.count.incrementAndGet();
	}

	@Override
	public void submitLP(final Executable<?> r) {
		TEConditions.validExecutable(r);
		if (r instanceof Runnable) {
			this.submitLP((Runnable) r);
		} else {
			this.submitLP(() -> {
				r.run_st();
			});
		}
	}

	/* EEX_executor_attemptable_submissions */

	@Override
	public boolean supportsAttemptableSubmissions() {
		return true;
	}

	@Override
	public boolean attempt_submit(final Runnable command) {
		if (command == null)
			return false;
		this.count.incrementAndGet();
		return true;
	}

	/* EEX_executor_attemptable_submissions2 */

	@Override
	public boolean supportsAttemptableSubmissions2() {
		return true;
	}

	@Override
	public <T> boolean attempt_submit2(final Executable<T> task) {
		this.count.incrementAndGet();
		return true;
	}

	/* EEX_executor_attemptable_batch_submissions */

	@Override
	public boolean supportsAttemptableBatchSubmissions() {
		return true;
	}

	@Override
	public boolean attempt_batch(final Runnable command) {
		if (command == null)
			return false;
		this.count.incrementAndGet();
		return true;
	}

	/* EEX_executor_attemptable_drain_submissions */

	@Override
	public boolean supportsAttemptableDrainSubmissions() {
		return true;
	}

	@Override
	public int attempt_drain(final Queue<Runnable> async_queue, final int batchSize) {
		if (async_queue == null || async_queue.isEmpty())
			return 0;
		if (batchSize <= 0)
			return 0;
		final Runnable[] rq = new Runnable[batchSize];
		for (int i = 0; i < batchSize; i++) {
			Runnable r = null;
			try {
				r = async_queue.poll();
			} catch (Throwable t) {
				t.printStackTrace();
			} finally {
				if (r == null)
					continue;
				rq[i] = r;
			}
		}
		this.count.addAndGet(rq.length);
		return rq.length;
	}

	/* EEX_query_executor_forking */

	@Override
	public boolean executorIsForking() {
		return ExecutorManager.ENABLE_BATCH_OPERATIONS;
	}

	/* EEX_compat_native_concurrency_api */

	@Override
	public Future<?> submit(final Runnable task) {
		TEConditions.validRunnable(task);
		final ExecutableRunnableFuture<Object> ef = new ExecutableRunnableFuture<Object>(task, null);
		execute(ef);
		return ef;
	}

	@Override
	public <T> Future<T> submit(final Runnable task, final T result) {
		TEConditions.validRunnable(task);
		final ExecutableRunnableFuture<T> ef = new ExecutableRunnableFuture<T>(task, result);
		execute(ef);
		return ef;
	}

	@Override
	public <T> Future<T> submit(final Callable<T> task) {
		final ExecutableFuture<T> ef = new ExecutableFuture<T>(task);
		execute(ef);
		return ef;
	}

	@Override
	protected <T> RunnableFuture<T> newTaskFor(final Callable<T> callable) {
		return new ExecutableFuture<T>(callable);
	}

	@Override
	protected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {
		TEConditions.validRunnable(runnable);
		return new ExecutableRunnableFuture<T>(runnable, value);
	}

	/* EEX_query_debug */

	@Override
	public Object debug_query(final EEXDebugQueryType type) {
		switch (Variables.requireNonNullObject(type, "type")) {
			case EEX_DEBUG_TOTAL_THREAD_COUNT:
				return 64;
			case EEX_DEBUG_ACTIVE_THREAD_COUNT:
				return 64;
			/* LPT */
			case EEX_DEBUG_LPT_ACTIVE:
				return true;
			case EEX_DEBUG_LPT_ACTIVE_THREAD_COUNT:
				return 64;
			case EEX_DEBUG_LPT_TOTAL_THREAD_COUNT:
				return 64;
			/* Queueing */
			case EEX_DEBUG_TASKS_IN_QUEUE:
				return this.count.get();
			/* */
			case EEX_DEBUG_ACTIVE_TASKS_STACK_TRACE: {
				return new StackTraceElement[0][0];
			}
			/* */
			default:
				return null;
		}
	}

	/* EEX_query_executor_large_batches */

	@Override
	public boolean executorAllowLargeBatches() {
		return true;
	}

	@Override
	public int executorPreferredBatchSize() {
		return 4;
	}

}
